﻿using System;
using System.Messaging;


namespace LogicStudio.Framework.MSMQ
{
    public class Queue<T> where T:class,new()
    {
        #region campos
        private System.Messaging.MessageQueue _colaPeticiones;
        private System.Messaging.MessageQueue _colaTransacciones;
        private string _mensajeError;
        private string _nombreColaPeticiones; //cola donde se depositaran las respuestas de las transacciones
        private string _nombreColaTransacciones; //cola donde se depositaran las transacciones
        #endregion

        #region propiedades

        /// <summary>
        /// instancia de la cola de peticiones
        /// </summary>
        public System.Messaging.MessageQueue ColaPeticiones
        {
            get { return _colaPeticiones; }
            set { _colaPeticiones = value; }
        }

        /// <summary>
        /// instancia de la cola de transacciones
        /// </summary>
        public System.Messaging.MessageQueue ColaTransacciones
        {
            get { return _colaTransacciones; }
            set { _colaTransacciones = value; }
        }

        
        /// <summary>
        /// mensaje de error provocado en el manejo de colas
        /// </summary>
        public string MensajeError
        {
            get { return _mensajeError; }
            set { _mensajeError = value; }
        }

        #endregion

        #region constructores

        /// <summary>
        /// Inicializa una unica cola MSMQ de petciones o transacciones
        /// </summary>
        /// <param name="nombre_cola">Nombre de la cola a crear o acceder si ya se encuentra creada</param>
        /// <param name="esPeticion">
        /// true si se desea crear una cola de peticiones
        /// false si se desea crear una cola de transacciones</param>
        public Queue(string nombre_cola, bool esPeticion)
        {
            if (esPeticion) //peticiones
            {
                this._nombreColaPeticiones = nombre_cola;
                //inicializa cola de peticiones en las colas privadas
                if (MessageQueue.Exists(@".\Private$\" + _nombreColaPeticiones))
                    _colaPeticiones = new System.Messaging.MessageQueue(@".\Private$\" + _nombreColaPeticiones);
                else
                    _colaPeticiones = MessageQueue.Create(@".\Private$\" + _nombreColaPeticiones);
                _colaPeticiones.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) });
            }
            else //transacciones
            {
                this._nombreColaTransacciones = nombre_cola;
                //inicializa cola de transacciones en las colas privadas
                if (MessageQueue.Exists(@".\Private$\" + _nombreColaTransacciones))
                    _colaTransacciones = new System.Messaging.MessageQueue(@".\Private$\" + _nombreColaTransacciones);
                else
                    _colaTransacciones = MessageQueue.Create(@".\Private$\" + _nombreColaTransacciones);
                _colaTransacciones.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) });
            }

        }


        /// <summary>
        /// Inicializa dos colas de mensajes MSMQ, una para peticiones y otra para transacciones
        /// </summary>
        /// <param name="nombre_cola_peticiones">Nombre de la cola de peticiones</param>
        /// <param name="nombre_cola_transacciones">Nombre de la cola de transacciones</param>
        public Queue(string nombre_cola_peticiones, string nombre_cola_transacciones)
        {
            this._nombreColaPeticiones = nombre_cola_peticiones;
            this._nombreColaTransacciones = nombre_cola_transacciones;
            //inicializa cola de peticiones en las colas privadas
            if (MessageQueue.Exists(@".\Private$\" + _nombreColaPeticiones))
                _colaPeticiones = new System.Messaging.MessageQueue(@".\Private$\" + _nombreColaPeticiones);
            else
                _colaPeticiones = MessageQueue.Create(@".\Private$\" + _nombreColaPeticiones);
            _colaPeticiones.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) });

            //inicializa cola de transacciones en las colas privadas
            if (MessageQueue.Exists(@".\Private$\" + _nombreColaTransacciones))
                _colaTransacciones = new System.Messaging.MessageQueue(@".\Private$\" + _nombreColaTransacciones);
            else
                _colaTransacciones = MessageQueue.Create(@".\Private$\" + _nombreColaTransacciones);
            _colaTransacciones.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) });
        }

        #endregion

        #region metodos publicos

        /// <summary>
        /// Coloca un mensaje en la cola MSMQ de peticiones
        /// </summary>
        /// <param name="bodyConsulta">Objeto a colocar en la cola, el objeto debe ser serializable</param>
        /// <param name="strIdConsulta">Id del proceso a encolar, se recomienda usar GUID</param>
        /// <returns>Id del mensaje encolado</returns>
        public string EncolarPeticion(Object bodyConsulta, string strIdConsulta)
        {
            string strRetorno = "";
            try
            {
                System.Messaging.Message mm = new System.Messaging.Message();
                mm.Body = bodyConsulta;
                mm.Label = strIdConsulta;
                //obtengo el id del mensaje
                strRetorno = mm.Id;
                _colaPeticiones.Send(mm);
            }
            catch (Exception ex)
            {
                strRetorno = "";
                this._mensajeError = ex.Message;
            }

            return strRetorno;
        }

        /// <summary>
        /// Coloca un mensaje en la cola MSMQ de transaccions
        /// </summary>
        /// <param name="bodyTransaccion">Objeto a colocar en la cola, el objeto debe ser serializable</param>
        /// <param name="strIdTransaccion">Id del proceso a encolar, se recomienda usar GUID</param>
        /// <returns>Id del mensaje encolado</returns>
        public string EncolarTransaccion(Object bodyTransaccion, string strIdTransaccion)
        {
            string strRetorno = "";
            try
            {
                System.Messaging.Message mm = new System.Messaging.Message();
                if (bodyTransaccion is MensajeWCFParaMSMQ)
                {
                    ((MensajeWCFParaMSMQ)bodyTransaccion).IdMensajeMSMQ = mm.Id;
                }
                mm.Body = bodyTransaccion;
                mm.Label = strIdTransaccion;
                mm.TimeToBeReceived = new TimeSpan(365, 0, 0, 0);
                //obtengo el id del mensaje
                strRetorno = mm.Id;
                

                _colaTransacciones.Send(mm);
            }
            catch (Exception ex)
            {
                strRetorno = "";
                this._mensajeError = ex.Message;
            }

            return strRetorno;
        }

        //metodos para leer mensajes
        /// <summary>
        /// Devuelve el primer mensaje que esta en cola sin removerlo de la cola
        /// </summary>
        /// <param name="colaMSMQ">Cola de la cual se extrae el mensaje</param>
        /// <returns>
        /// Null si no hay mensjes en la cola
        /// Instabcia de Message si hay mensajes en la cola </returns>
        public System.Messaging.Message ExtraerMensajeCola(MessageQueue colaMSMQ)
        {
            System.Messaging.Cursor cursor = colaMSMQ.CreateCursor();
            try
            {
                //mensajes de consulta

                System.Messaging.Message m = PeekWithoutTimeout(colaMSMQ, cursor, PeekAction.Current);
                return m;
            }
            catch (Exception ex)
            {
                this._mensajeError = ex.Message;
                return null;
            }
        }

        /// <summary>
        /// Retorna el mensaje de la cola MSMQ y lo remueve de la cola
        /// </summary>
        /// <param name="colaMSMQ">Cola de la cual se va a extraer y remover el mensaje</param>
        /// <param name="IdMensaje">Id del mensaje a extraer y remover</param>
        /// <returns>
        /// Null, si el id no exsite en la cola 
        /// una instancia de Message si existe dicho id en la cola</returns>
        public System.Messaging.Message RemoverMensajeCola(MessageQueue colaMSMQ, string IdMensaje)
        {
            System.Messaging.Message ret = null;
            try
            {
                ret = colaMSMQ.ReceiveById(IdMensaje);
            }
            catch (Exception ex)
            {
                this._mensajeError = ex.Message;
            }
            return ret;

        }

        /// <summary>
        /// Extrae el cuerpo del mensaje
        /// </summary>
        /// <param name="colaMSMQ"></param>
        /// <returns></returns>
        public T ExtraeCuerpoMensajeCola(MessageQueue colaMSMQ)
        {
            try
            {
                var mensaje = ExtraerMensajeCola(colaMSMQ);
                T cuerpoMensaje = (T)mensaje.Body;
                return cuerpoMensaje;
            }
            catch (Exception ex)
            {
                MensajeError = ex.Message;
                return null;
            }
        }
        #endregion
        #region metodos privados

        /// <summary>
        /// extrae un mensaje de la cola sin tiempo de time out
        /// </summary>
        /// <param name="colaMSMQ">Cola a procesar</param>
        /// <param name="cursor">cursosr de la cola a procesar</param>
        /// <param name="action">accion a ejecutar</param>
        /// <returns></returns>
        private System.Messaging.Message PeekWithoutTimeout(
                                                    MessageQueue colaMSMQ,
                                                    System.Messaging.Cursor cursor,
                                                    PeekAction action
                                                )
        {
            System.Messaging.Message ret = null;
            try
            {
                ret = colaMSMQ.Peek(new TimeSpan(1), cursor, action);
            }
            catch (Exception ex)
            {
                this._mensajeError = ex.Message;
            }
            return ret;
        }
        #endregion

        
    }
}
